package ooo.reindeer.commons.service;

import ooo.reindeer.commons.Property;
import ooo.reindeer.commons.utils.ReflectUtils;
import ooo.reindeer.logging.ILogger;
import ooo.reindeer.logging.LogUtil;

import java.util.ConcurrentModificationException;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

@SuppressWarnings("unchecked")
public class ServiceUtils {

    private static final ILogger LOGGER = LogUtil.getLogger(ServiceUtils.class);
    private static Map<String, Object> serviceMap = new ConcurrentHashMap<String, Object>();
    private static Map<String, AtomicInteger> serviceLoadCountMap = new ConcurrentHashMap<String, AtomicInteger>();
    private static Map<String, AtomicBoolean> serviceStatusMap = new ConcurrentHashMap<String, AtomicBoolean>();
    private static Vector<IServiceFinder> externalServiceFinders = new Vector<IServiceFinder>();

    private ServiceUtils() {
        throw new RuntimeException("Does not support instantiation");
    }

    public static boolean addServiceFinder(IServiceFinder findeService) {
        return externalServiceFinders.add(findeService);
    }

    private static Object findServicebyExternalServiceFinders(String key) {
        Object service = null;
        boolean reFind;
        do {
            reFind = false;
            try {
                for (IServiceFinder findeService : externalServiceFinders) {
                    service = findeService.findService(key);
                    if (service != null) {
                        break;
                    }
                }
            } catch (ConcurrentModificationException e) {
                reFind = true;
            }
        } while (reFind);
        return service;
    }

    public static <V> V getService(String key) {

        Object service = serviceMap.get(key);
        if (service == null) {
            service = findServicebyExternalServiceFinders(key);
        }
        if (service == null) {
            throw new RuntimeException(key + " not found");
        } else {
            return (V) service;
        }
    }

    public static void loadService(Property servicesConfig) {

        Set<String> serviceIds = servicesConfig.getKeySet();

        for (String serviceId : serviceIds) {
            Property serviceConfig = servicesConfig.getProperty(serviceId);
            loadService(serviceId, serviceConfig);

        }
        LOGGER.debug("{}    {}", "TYPE_INIT", "successful");
    }

    private static void loadService(String serviceId, Property serviceConfig) {
        String service_class = serviceConfig.value();

        if (!(loadServiceCount(serviceId) > 1)) {

            Object service = ReflectUtils.newObject(service_class);
            if (service instanceof IService) {
                ((IService) service).init(serviceConfig);
            }

            putService(serviceId, service);
            LOGGER.debug("{}    {}", "TYPE_INIT", serviceId);

        }
    }

    private synchronized static int loadServiceCount(String serviceId) {
        AtomicInteger count = serviceLoadCountMap.get(serviceId);
        if (count == null) {
            count = new AtomicInteger(1);
            serviceLoadCountMap.put(serviceId, count);
            return 1;

        } else {
            return count.incrementAndGet();
        }
    }

    private static Object putService(String key, Object value) {
        return serviceMap.put(key, value);
    }

    private synchronized static boolean setServiceStatus(String serviceId, boolean status) {
        AtomicBoolean count = serviceStatusMap.get(serviceId);
        if (count == null) {
            count = new AtomicBoolean(false);
            serviceStatusMap.put(serviceId, count);

        }
        return count.getAndSet(status);

    }

    public static void start() {
        Set<String> set = serviceMap.keySet();

        for (String serviceId : set) {
            start(serviceId);
        }
        LOGGER.debug("{}    {}", "TYPE_START", "successful");
    }

    public static void start(String serviceId) {
        Object serviceObject = serviceMap.get(serviceId);
        if (serviceObject instanceof IService) {
            if (!setServiceStatus(serviceId, true)) {
                ((IService) serviceObject).start();
                LOGGER.debug("{}    {}", "TYPE_START", serviceId);
            }
        }
    }

    public static void stop() {

        Set<String> set = serviceMap.keySet();

        for (String serviceId : set) {
            stop(serviceId);
        }
        LOGGER.debug("{}    {}", "TYPE_STOP", "successful");

    }

    public static void stop(String serviceId) {
        Object serviceObject = serviceMap.get(serviceId);
        if (serviceObject instanceof IService) {
            if (unLoadserviceCount(serviceId) <= 0) {
                if (setServiceStatus(serviceId, false)) {
                    ((IService) serviceMap.remove(serviceId)).stop();
                    LOGGER.debug("{}    {}", "TYPE_STOP", serviceId);
                }
            }
        }
    }

    private synchronized static int unLoadserviceCount(String serviceId) {
        AtomicInteger count = serviceLoadCountMap.get(serviceId);
        if (count == null) {
            count = new AtomicInteger(0);
            serviceLoadCountMap.put(serviceId, count);
            return 0;

        } else {
            return count.decrementAndGet();
        }
    }
}
